Kubernetes与分布式系统,容器化背后的故事
原文链接:https://www.infoq.com/articles/distributed-systems-kubernetes/
我想为这次演讲预先设置一些背景,在这里当我提到分布式系统时,我所指的是由多个组件组成的系统,可能会有数百个这样的组件。这些组件可能是有状态的、无状态的或者是无服务器的。除此之外,这些组件可以使用不同的语言创建,运行在混合环境之中,开发时使用的是开源技术和开发标准,支持互操作性。我相信你也可以使用闭源的软件创造这样的系统,或者在 AWS 和其他的地方创建它们。具体到这次演讲,我会特别关注 Kubernetes 生态系统,以及如何在 Kubernetes 平台上创建这样的系统。
我们从分布式系统的需求开始。我所想的是我们想要创建一个应用或者服务,并编写一些业务逻辑。那么,我们需要从平台和运行时环境得到哪些支撑以构建分布式系统呢?从基础来讲,最初的需求是我们需要一些生命周期管理的能力。当我们使用任意语言编写应用的时候,我们希望能够稳定地打包和部署应用,进行回滚和健康检查。并且能够将应用放到不同的节点上,进行资源隔离、扩展、配置管理,以及所有类似这样的事情。这些都是我们创建分布式应用所需要的最基本的东西。
第二个基石是网络相关的。我们有了一个应用之后,就希望它能够可靠地连接到其他的服务上,不管其他的服务在集群内还是在集群外部。我们希望能有诸如服务发现、负载均衡等能力,我们还希望能够进行流量转移,不管是基于不同的发布策略还是其他的一些原因。在此之后,我们希望能够与其他的系统进行弹性通信,不管是通过重试、超时、断路器还是其他方式来实现。当然,还要有安全保障,要有足够的监控、跟踪、可观察性等等。
有了网络之后,下一件事就是我们希望能够与不同的 API 和端点进行对话,也就是资源绑定:与其他协议和不同的数据格式进行对话。甚至可能从一种数据格式转换成另外一种数据格式。在这里我还想要包括像轻量级过滤(light filtering)这样的事情,也就是当订阅一个主题的时候,我们可能只关心特定的事件。
你认为最后一类会是什么呢?它就是状态。当我提到状态和状态化抽象的时候,我讨论的不是实际的状态管理,比如数据库或文件系统所做的事情。我更多的是讨论开发人员对那些幕后依赖状态的功能的抽象。也许,你需要进行工作流管理。可能你想要管理长时间运行的进程,或者要进行时间调度,或者要实现一些 cron job 来定期运行服务。也许,你想要实现分布式缓存、保证幂等性或者能够回滚。所有的这些都是开发人员层面的原语(primitive),但是在幕后,它们依赖于一些状态。我们希望能拥有这些抽象来创建健壮的分布式系统。
我们将会使用这个分布式系统原语的框架来评估在 Kubernetes 和其他项目上这些内容的变化。
假设我们从单体架构开始,考虑如何获取这些能力。在这种情况下,当我提到单体的时候,在分布式应用这个上下文中,我想到的是 ESB。ESB 的功能是非常强大的,当检查我们的需求列表时,我会说 ESB 对所有有状态的抽象都有很好的支持。
借助 ESB,我们可以实现长期运行的进程的编排、实现分布式事务、回滚和幂等性。此外,ESB 还提供了出色的资源绑定的能力,它们具备上百个连接器,支持转换、编排,甚至网络方面的能力。最后,ESB 甚至可以实现服务发现和负载均衡。
它具备网络连接可靠性方面所有的功能,所以它能够实现重试。可能从本质来讲,ESB 并不是分布式的,所以它不需要非常高级的网络和发布功能。ESB 缺乏的主要是生命周期管理。因为它是一个单运行时,第一个问题就是你会被限制使用单一的语言。这通常对应真正的运行时在构建中所使用的语言,比如 Java 或.NET 等。然后,因为它是一个单运行时,我们无法很容易地进行声明式部署或自动放置(automatic placement)。部署是非常庞大和重量级的,所以通常会涉及到人工的交互。而这样一个单体架构所带来的另外一个困难就是扩展性。“我们无法扩展单个组件。”
最后同样重要的是,关于隔离,不管是资源隔离还是故障隔离,在单体架构下都是无法实现的。从我们所需的框架来看,ESB 单体架构并不合格。
接下来,我建议我们看一下云原生架构以及这些需求是如何变化的。如果我们从一个比较高的层面来看这些架构的变化的话,就会发现云原生可能是从微服务运动开始的。微服务允许我们将一个单体应用按照业务域进行拆分。实践证明,容器和 Kubernetes 实际上是管理这些微服务的绝佳平台。接下来,我们看一下 Kubernetes 有哪些具体的特性和能力,使其成为微服务领域特别有吸引力的方案。
最初,能够进行健康探测是 Kubernetes 流行起来的原因。在实践中,这意味着当我们在一个 pod 中部署容器的时候,Kubernetes 将会检查进程的健康状况。通常情况下,这样的进程模型是不够好的。我们仍然可能会有一个正在运行的进程,但它的状态并不健康。这就是为何还需要使用就绪状态(readiness)和活跃状态(liveness)检查的原因。在启动的时候,Kubernetes 将会进行就绪检查来确定应用程序何时能够开始接受流量。它还会进行活跃性检查,以持续检查应用的健康状况。在 Kubernetes 之前,这种方式并不流行,但是如今几乎所有的语言、框架和运行时环境都有健康检查的功能,通过一个端点可以快速实现。
Kubernetes 引入的另外一件事情就是应用程序的生命周期管理,在这里我的意思是,你不用再去控制服务何时启动以及何时关闭了。我们可以相信平台能够帮我们做到这一点。Kubernetes 能够启动应用,能够关掉应用,也能将其在不同的节点间进行转移。要实现这一点,我们就必须正确地实现平台在启动和关闭时告诉我们的事件。
Kubernetes 使之流行起来的另外一件事情就是部署以及以声明式的方式进行部署。这意味着,我们不必再去自己启动服务并根据日志判断它是否已经启动完毕。我们也不必再去手动升级实例,Kubernetes 的声明式部署就可以帮我们做到这一点。根据你所选择的策略,它能够停掉旧的实例并启动新的实例。除此之外,如果出现问题的话,它还能够回滚。
另外一件事情就是声明所需的资源。当创建服务的时候,我们会将其容器化。有一项好的实践是告诉平台该服务需要多少 CPU 和内存。Kubernetes 会利用这些信息为我们的工作负载找到最合适的节点。在 Kubernetes 出现之前,我们必须根据自己的标准手动将实例放到节点中。现在,我们可以根据自己的偏好设置指导 Kubernetes,它将会为我们做出最佳的决策。
如今,在 Kubernetes 上,我们可以支持多种方式的配置管理。我们的应用程序运行时不需要进行任何的配置查找。Kubernetes 会确保配置内容将会最终出现在工作负载所在的节点上。配置被会映射为一个卷或者环境变量,供应用程序使用。
实际上,我刚才提到的这些具体的能力也是互相关联的。例如,如果你希望实现服务的自动放置,那么就需要将服务的资源需求告诉 Kubernetes。然后,你需要告诉它使用什么样的部署策略。为了让该策略能够正常运行,应用程序必须要实现来自环境的事件。它必须要实现健康检查。一旦我们将所有的最佳实践准备就绪并使用好这些能力,那么我们的应用就会成为一个优秀的云原生公民,它就可以在 Kubernetes 上实现自动化(这代表了在 Kubernetes 上运行工作负载的基础模式)。最后,关于在 pod 中如何组织容器、配置管理和行为等方面还有其他的模式。
我想简要讨论的下一个话题是关于工作负载的。从生命周期的角度来看,我们希望能够运行不同的工作负载。我们在 Kubernetes 上也可以做到这一点。运行十二要素应用(Twelve-Factor App)和无状态微服务是非常简单的。Kubernetes 可以做到这一点。但这并不是唯一的工作负载类型。也许你还会有有状态的工作负载,在 Kubernetes 上我们可以通过 stateful set 实现它。
我们想要运行的另外一种工作负载可能是单例应用。我们想要在整个集群中运行该应用的唯一一个实例,我们希望它是一个可靠的单例应用。当它出现故障时,应该会再次重新启动。因此,你可以根据需要,以及这个应用应该最少要有一个实例还是最多有一个实例的语义,在 stateful set 和 replica set 之间做出选择。还有一种工作负载就是 job 和 cron job,借助 Kubernetes,它们也是可以实现的。
如果我们将 Kubernetes 所有的这些特性映射到我们的需求上,那么可以说 Kubernetes 满足了生命周期的要求。通常情况下,我所创建的需求列表的内容是由 Kubernetes 迄今为我们提供的功能所驱动的。这是所有平台的预期能力,Kubernetes 能够为部署所提供的特性包括配置管理、资源隔离以及故障隔离。除此之外,它还支持不同的工作负载,不过无服务器本身除外。
这就是 Kubernetes 为开发人员提供的所有功能,那么我们该如何扩展 Kubernetes?如何让它为我们提供更多的特性呢?因此,我想要描述我们目前常用的两种方式。
首先要提及的就是 pod 的概念,它是一种用来在节点上部署容器的抽象机制,pod 能够为我们保证如下两点:
第一个就是部署方面的保证:pod 中的所有容器最终将会位于相同的节点上。这意味着它们彼此之间可以使用 localhost、基于文件系统的异步方式或者其他 IPC 机制进行通信。
pod 给我们的另外一个保证是关于生命周期的,pod 中所有的容器并不是平等的。
根据使用的是 inic动的时候,它们会一个接一个地按顺序执行。只有当前面的容器成功完成后,后面的容器才会运行。它们有助于实现由容器驱动的类似工作流的逻辑。
而应用容器则是并行运行的。它们会在 pod 的整个生命周期中运行,这是 sidecar 模式的基础。sidecar 可以运行多个容器,它们相互协作,共同为用户提供价值。这就是我们目前能够看到的扩展 Kubernetes 以获取额外能力的主要机制。
为了阐述下面的能力,我必须要简单介绍一下 Kubernetes 内部的运行方式。它是基于一种叫做调谐循环(reconciliation loop)的机制实现的。调谐循环的理念就是将期望状态驱动到实际状态。在 Kubernetes 中,有很多功能都是基于这种机制实现的。例如,当我想要两个 pod 实例的时候,这就是系统的期望状态。Kubernetes 会有一个持续运行的控制循环,它会检查是否有两个 pod 的实例在运行。如果实际运行的实例数量不是两个的话,它会计算差异,看实例的数量是只有一个还是超过了两个。它会确保最终有两个实例。
这方面的例子还有很多,有些是关于 replica set 或 stateful set 的。资源定义会映射到具体的控制器,每种资源定义都会有一个控制器。控制器能够确保现实状况会匹配期望,你甚至可以编写自定义的控制器。
当在 pod 中运行应用的时候,我们无法在运行时加载配置文件的变化。但是,我们可以编写自定义的控制器,让它监控 config map 的变化并重新启动 pod 和应用,这样我们就能够获取到配置的变更了。
实践证明,即便 Kubernetes 有一个很好的资源集合,它也无法满足我们所有不同的需求,因此 Kubernetes 引入了自定义资源定义(custom resource definition)的概念。这意味着我们可以建模自己的需求,并定义 Kubernetes 中运行的 API。它与其他的 Kubernetes 原生资源是共存的。我们可以使用任何语言编写自己的控制器,它们只需要理解我们的模型即可。我们可以使用 Java 实现一个 ConfigWatcher,描述我们前面所阐述的内容。这也就是 operator 模式,一个与自定义资源定义一起运行的控制器。如今,我们看到已经有了很多的 operator,这是扩展 Kubernetes 实现额外能力的第二种方式。
接下来,我想要介绍几个在 Kubernetes 之上构建的几个平台,它们大量使用了 sidecar 和 operator 来为开发者提供额外的能力。
我们首先从服务网格开始,那什么是服务网格呢?
我们有两个服务,服务 A 想要调用服务 B,而且可能会使用任意的语言。假设这就是我们的工作负载。服务网格使用了 sidecar 控制器并将一个代理注入到了我们的服务旁边。这样在 pod 中就有了两个容器。代理是透明的,我们的应用完全不会感知到代理的存在,但是它会拦截所有传入和传出的流量。另外,代理还充当了数据防火墙的角色。
这些服务代理的集合代表了数据平面,它们通常是非常小且无状态的。为了获取所有的状态和配置,它们会依赖于控制平面。控制平面是有状态的部分,它会保持所有的配置,收集指标,做出决策并与数据平面进行交互。实际上,我们还需要另外一个组件,也就是将数据输入到集群的 API 网关。有些服务网格有自己的 API 网关,有些则会使用第三方的网关。但是,如果你查看一下所有的这些组件的话,会发现它们都提供了我们想要的能力。
API 网关主要的关注点在于抽象我们的服务实现。它隐藏了细节并提供了边界能力。服务网格所做的事情则恰好相反。在某种程度上,它增强了服务中的可见性和可靠性。联合起来,我们可以说 API 网关和服务网格满足网络方面的所有需求。为了获取 Kubernetes 之上的网络能力,单纯使用服务是不够的,我们还需要服务网格。
我想讨论的下一个话题是 Knative,这是由谷歌在几年前发起的项目。它是在 Kubernetes 之上的一个层,提供了无服务器的能力,它包括两个主要的模块:
Knative Serving:主要关注请求 - 答复的交互
Knative Eventing:更多地用于事件驱动的交互。
我们来简单体会一下 Knative Serving 是什么。借助 Knative Serving,我们会定义一个服务,但是它与 Kubernetes 服务不同,这是一个 Knative 服务。使用 Knative 服务定义完工作负载之后,就会得到一个具备无服务器特性的部署(deployment)。我们不需要建立和运行实例,请求抵达时,它可以从零开始启动。这样我们就具有了无服务器的能力,它可以快速扩展,也可以收缩至零。
Knative Eventing 为我们提供了一个完全声明式的事件管理系统。假设我们有一些想要集成的外部系统和一些外部的事件生产者,在底层,我们让自己的应用运行在一个容器中,它具有一个 HTTP 端点。借助 Knative Eventing,我们可以启动一个 broker,它可以触发一个映射到 Kafk 的 broker,也可以触发内存或云服务。除此之外,我们还可以启动一个导入器(importer),将其连接到外部系统上并将事件导入到我们的 broker 中。比如,这些导入器可以基于 Apache Camel,它有数百个连接器。
事件进入到 broker 之后,我们就可以通过 YAML 声明的形式,让我们的容器订阅这些事件。在我们的容器中,并不需要任何的消息客户端,如 Kafka 客户端。我们的容器会以 HTTP POST 的形式得到事件。这是一个完全由平台托管的消息基础设施。作为开发人员,我们只需要在容器中编写业务逻辑,而不需要处理任何消息相关的逻辑。
从我们的需求来看,Knative 满足了其中的一些。从生命周期的角度来看,它给了我们的工作负载无服务器的能力,所以能够收缩至零,并从零开始进行扩展。从网络的角度来看,它与服务网格有一些重叠,Knative 也能进行流量转移。从绑定的角度来看,它对使用 Knative 导入器的绑定提供了很好的支持。它还能为我们提供 Pub/Sub 或点到点的交互,甚至某些顺序化(sequencing)处理的功能。它满足了一些类别的需求。
另外一个使用 sidecar 和 operator 的项目是 Dapr,它是由微软在最近发起的一个项目,并且正在快速流行起来。1.0 版本已经被认为可以投入生产环境使用了。这是一个 sidecar 形式的分布式系统工具集,Dapr 中的任何内容都是以 sidecar 的形式提供的,并且具有一组他们称之为构建块(building block)或者能力的特性。
那 Dapr 的这些能力是什么呢?第一组能力是关于网络的。Dapr 能够实现服务发现以及服务间的点到点集成。类似的,它还能够进行跟踪、可靠性通信、重试以及恢复。第二组能力是关于资源绑定的:
它有大量到云 API 和不同系统的连接器
还能实现消息的发布 / 订阅以及其他逻辑。
有意思的是,Dapr 还引入了状态管理的概念。除了 Knative 和服务网格提供的功能之外,Dapr 还在状态存储之上进行了抽象。除此之外,我们还可以在存储机制的支持下与 Dapr 进行基于键值的交互。
从较高的层级来看,这种架构是将我们的应用放在最顶层,而应用可以采用任意的语言。我们可以使用 Dapr 提供的客户端库,但并不强制要求这样做。我们可以使用语言本身的特性来发起 HTTP 和 gRPC 调用 sidecar。它与服务网格差异在于 Dapr sidecar 并不是一个透明的代理。它是一个显式的代理,我们必须要在应用中调用它,并通过 HTTP 或 gRPC 进行交互。根据我们所需要的能力,Dapr 可以与其他的系统进行交互,比如云服务。
在 Kubernetes 上,Dapr 以 sidecar 的形式进行部署,它也可以在 Kubernetes 之外运行(它并非只能用在 Kubernetes 中)。除此之外,它还有一个 operator,sidecar 和 operator 是主要的扩展机制。还有一些其他的组件用来管理证书、处理基于 actor 的模型以及注入 sidecar。我们的工作负载与 sidecar 进行交互,并且要完成与其他服务的对话,从而实现与不同云供应商的交互性。它还为你提供了额外的分布式系统的能力。
如果让我总结一下这些项目为我们提供了什么的话,那么我们可以说 ESB 是分布式系统的早期形态,在这里我们具有中心化的控制平面和数据平面,但是无法很好地进行扩展。在云原生中,我们依然有一个中心化的控制平面,但是数据平面是去中心化的,而且是通过良好的隔离能够实现高度的可扩展性。
我们始终会需要 Kubernetes 进行良好的生命周期管理,在此之上,我们可能会需要一个或多个附加组件。也许我们需要 Istio 实现高级的网络,需要使用 Knative 实现无服务器的工作负载,或者需要 Dapr 进行集成。这些框架能够与 Istio 和 Envoy 进行很好的协作。从 Dapr 和 Knative 的角度来看,我们可能必须要从中选择一个。将它们联合起来,我们就以云原生的方式提供了过去 ESB 上所拥有的功能。
在接下来的部分中,我提供了一个带有一定倾向性的项目列表,我认为这些领域的开发状态非常令人兴奋。
我想首先从生命周期开始。借助 Kubernetes,我们可以为应用实现非常有用的生命周期管理,但是对于复杂的生命周期管理来讲,这可能是不够的。例如,如果你有一个更复杂的有状态应用,那么 Kubernetes 中的部署原语对你的应用程序来说就是不够的。
在这些场景下,我们就可以使用 operator 模式。我们可以使用 operator 进行部署和升级,也可以进行备份,可能会是将服务存储到 S3 上。除此之外,你可能还会发现 Kubernetes 中的健康检查机制不够好,比如仅有活跃性检查和就绪检查还是不够的。在这种情况下,我们可以借助 operator 实现更智能的活跃性检查和就绪性检查,并基于此进行恢复。
第三个领域是自动扩展和调优。你可以实现一个能够更好地理解你的应用的 operator,从而能够在平台上自动调优。如今,有两个主要的框架来编写 operator,分别是来自 Kubernetes 特别兴趣小组的 Kubebuilder 以及 Red Hat 所创建的 operator 框架所包含的 Operator SDK。它包含下面几个组成部分:
Operator SDK 允许我们编写 operator,Operator Lifecycle Manager 能够管理 operator 的生命周期,而 OperatorHub 则可以发布 operator。如果感兴趣的话,我们可以看到上百个管理数据库、消息队列和监控工具的 operator。从生命周期角度来看,operator 可能是在 Kubernetes 生态系统中最活跃的开发领域。
我选择的另外一个项目是 Envoy。服务网格接口的引入能够让我们更容易地切换不同的服务网格实现。在部署方面,对 Istio 有了一些整合。现在,你不需要部署七个 pod 了,而只需要部署一次。更有意思的是 Envoy 项目在数据平面上所发生的变化。我们看到越来越多的第七层协议添加到了 Envoy 中。
服务网格添加了对更多协议的支持,比如 MongoDB、ZooKeeper、MySQL、Redis,以及最近的 Kafka。我看到 Kafka 社区现在正在进一步改善他们的协议,以便于对服务网格更加友好。我们可以期待,将会有更紧密的集成以及更多的能力。最可能出现的情况是,会有一些桥接能力。我们可以在应用程序的本地发起一个 HTTP 调用,而代理将会在幕后使用 Kafka。我们还可能会在应用程序之外,在 sidecar 中为 Kafka 协议进行转换和加密。
另外一个令人兴奋的进展是引入了 HTTP 缓存。现在,Envoy 可以进行 HTTP 缓存。在我们的应用中,不需要使用任何的缓存客户端。所有的这些都会在 sidecar 中透明地完成。这里会有窃听过滤器(tap filter),所以我们可以窃听流量并得到流量的副本。最近,随着 WebAssembly 的引入,这意味着如果你想为 Envoy 写一些自定义的过滤器,我们不必用 C++ 来编写,也不必编译整个 Envoy 运行时。我们可以用 WebAssembly 编写过滤器,并在运行时部署。其中大部分功能仍在进行中。他们没有固步自封,这说明数据平面和服务网格没有裹足不前,只支持 HTTP 和 gRPC。他们有兴趣支持更多的应用层协议,从而让我们支撑更多的用例。最主要的是,随着 WebAssembly 的引入,我们现在可以在 sidecar 上编写自定义逻辑。这种方式很好,只要你不把一些业务逻辑放在那里就行。
Apache Camel 是一个进行集成的项目,它用很多的连接器,能够连接到使用企业级集成模式的不同系统中。Camel 3.0 版本已经深度集成了 Kubernetes,并使用了相同的原语,比如我们提到的 operator。
我们可以在 Camel 中使用 Java、JavaScript 或 YAML 等语言编写集成逻辑。最新版本引入了一个在 Kubernetes 中运行并能理解我们的集成需求的 Camel operator。当我们在编写 Camel 应用程序时,会将其部署到一个自定义的资源中,然后 operator 就能知道如何构建容器或找到依赖。根据平台的能力,无论是单纯的 Kubernetes,还是与 Knative 结合的 Kubernetes,它都能决定使用什么服务以及如何实现我们的集成需求。在我们的运行时之外,会有相当多的智能处理,但是它们都在 operator 中,所有的这一切运行都非常快。为什么我将其称之为一个绑定方面的新趋势呢?主要是因为 Apache Camel 的能力都是由它提供的连接器实现的。这里有趣的一点是它如何与 Kubernetes 深度整合。
我想要讨论的另一个项目是 Cloudstate 以及状态相关的新趋势。Cloudstate 是 Lightbend 的一个项目,主要专注于无服务器和函数驱动的开发。在最新版本中,他们正在使用 sidecar 和 operator 与 Kubernetes 进行深度集成。
这里的理念在于,当我们编写函数的时候,在函数中所要做的就是使用 gRPC 获取状态,然后与状态进行交互。所有的状态管理是在一个 sidecar 中进行的,这个 sidecar 与其他的 sidecar 形成了集群。借助它,我们可以实现事件溯源、CQRS、键值查找和消息功能。
从我们的应用程序的角度来看,我们并不了解所有这些复杂的事情。我们要做的只是调用一个本地的 sidecar,而 sidecar 会处理这些复杂的问题。在幕后,它可以使用两个不同的数据源,而且它拥有所有的有状态抽象,而这是我们作为开发人员所需要的。
到目前为止,我们已经看到了云原生生态系统的现状和一些仍在进行中的最新进展。我们该如何理解这一切呢?
如果我们看一下微服务在 Kubernetes 上的样子,就会发现我们需要一些平台性的功能。此外,我们还需要使用 Kubernetes 的功能,主要用于生命周期管理。然后,我们很可能会透明地使用一些服务网格,比如 Envoy,以得到增强的网络能力,不管是流量路由、弹性、增强的安全性,还是为了监控的目的。除此之外,根据我们的使用场景,可能还需要 Dapr 或 Knative,这取决于工作负载。所有的这些都是进程外的附加能力。我们剩下的就是编写业务逻辑了,不过这不是在这些基础设施之上,而是在另外一个单独的运行时中。最有可能出现的是,未来的微服务将是多个容器组成的多运行时,其中有些是透明的,有些是显式使用的。
如果看得再深入一点会是什么样子呢,我们使用某种高级别的语言编写业务逻辑。至于是什么语言并不重要,它不一定必须是 Java,因为我们可以使用任意的语言来开发自定义逻辑。
业务逻辑与外部世界的所有交互都是通过 sidecar 进行的,sidecar 会与平台集成并进行生命周期管理。它为外部系统进行网络抽象,并提供高级绑定能力和状态抽象。sidecar 不需要我们进行开发。我们可以直接选取它,并使用 YAML 或 JSON 进行配置,然后就可以使用它了。这意味着我们可以很容易地更新 sidecar,因为它不会嵌入到我们的运行时中。这样的话,打补丁和更新会更容易。它为我们的业务逻辑实现了多语言运行时。
回到我最初的问题,微服务之后将是什么呢?
如果我们看一下架构是如何发展的,会发现从较高的层次来看,应用架构是从单体应用开始的。然而,微服务为我们提供了如何将单体应用拆分成独立的业务域的指导原则。从此之后,出现了无服务器和函数即服务(FaaS),我们可以进一步将系统拆分为操作(operation),这带来了极强的扩展性,因为我们可以独立扩展每个操作了。
我想说的是,也许 FaaS 并不是最好的模型,因为函数并不是实现复杂服务的最佳模型,在这种情况下,如果多个操作与同一个数据集进行交互的话,我们希望能够将它们放到一起。如果是这样的话,也许可以考虑我所说的 Mecha 架构 多运行时,在这种架构中,我们将业务逻辑放到一个容器中,并将所有基础设施相关的关注点作为一个单独的容器。它们共同代表了一个多运行时微服务。这可能是更为合适的模型,因为它具有更好的属性。
我们可以得到微服务的所有好处,但我们依然将所有的领域、所有的限界上下文放到了同一个地方。我们在一个单独的容器中放置了所有的基础设施和所需的分布式应用,在运行时,我们会将它们组合在一起。目前,与之最接近的可能是 Dapr。他们正在遵循这种模式。如果你只对网络方面感兴趣,使用 Envoy 可能也会接近这种模式。
- END -
点亮,服务器三年不宕机